/*
 * sys_env.c
 *
 *  Created on: 2017年9月14日
 *      Author: Administrator
 */

#include "sys_env2.h"

#include "sys_mem.h"
#include "sys_utils.h"

/*----------------------------------------------------------------------------*/
typedef struct
{
    uint32_t r :16;/* 记录条数 */
	uint32_t addr;/* 数据地址 */
    uint32_t len :8;/* 第r条数据长度 */
} env2_stat_t;

/*----------------------------------------------------------------------------*/
static int16_t _env2_del = -1;
static int16_t _env2_delete = -1;
static uint8_t *env2_data = NULL;
static uint8_t env2_dlen = 0;

static env2_err_t _env2_save_r(int no, uint8_t area, void *val, uint8_t len);
static env2_stat_t *env2_stat(int no, uint8_t area);
static int env2_addr(int no, uint8_t area, int n, env2_stat_t *stat);
static int env2_repair(int no, uint8_t area, env2_stat_t *stat);

/*----------------------------------------------------------------------------*/
env2_err_t env2_load(int no, uint8_t area, void *val, uint8_t *size)
{
    uint8_t sz;
    env2_err_t err;
	uint8_t *buf;
    env2_stat_t *stat;

    if (no < 0 || ENV2_SECTOR_START + no > ENV2_SECTOR_END || area >= ENV2_AREA)
        return (ENV2_ERR_INVALID);

    if (val != NULL && (!size || !*size))
        return ENV2_ERR_INVALID;

    stat = env2_stat(no, area);
	if (stat == NULL)
        return (ENV2_ERR_IO);

	buf = NULL;
	sz = 0;
    err = ENV2_ERR_OK;
	while (stat->r)
	{
        if (!val || !size)
        {
            if (!buf || !sz)
            {
                sz = stat->len + 2;
                buf = (uint8_t*) mem_malloc(sz);
                if (buf == NULL)
                    return (ENV2_ERR_NOMEM);
            }
            else if (stat->len + 2 > sz)
            {
                sz = stat->len + 2;
                buf = (uint8_t*) mem_realloc(buf, sz);
                if (buf == NULL)
                    return (ENV2_ERR_NOMEM);
            }
        }
        else
		{
			if (buf == NULL || sz == 0)
			{
                sz = stat->len + 2;
				buf = (uint8_t*) mem_malloc(sz);
				if (buf == NULL)
                    return (ENV2_ERR_NOMEM);
			}
            else if (stat->len + 2 > sz)
			{
                sz = stat->len + 2;
				buf = (uint8_t*) mem_realloc(buf, sz);
				if (buf == NULL)
                    return (ENV2_ERR_NOMEM);
			}
		}

        if (env2_flash_read(stat->addr, buf, stat->len + 2) != 0)
		{
            err = ENV2_ERR_IO;
		}
		else if (crc16_ccit_calc_s(buf, stat->len + 2) != 0)
		{
            err = ENV2_ERR_CRC;
			for (; stat->r != 0;)
			{
                if (--stat->r && env2_addr(no, area, stat->r, stat) == 0)
					break;/* 取到正确的地址 */
			}
		}
        else if (err == ENV2_ERR_CRC)
		{
            uint8_t *buff = (uint8_t*) mem_malloc(stat->len + 4);
            if (buff != NULL)
            {
                uint16_t crc16;

                env2_delete(no, area);
                stat->addr = (ENV2_SECTOR_START + no) * ENV2_SECTOR_SZ;
                stat->addr += (uint32_t) area * ENV2_AREA_SZ;
                buff[0] = ENV2_MAGIC;
                buff[1] = stat->len;
                crc16 = crc16_ccit_calc_s(buf, stat->len);
                memcpy(&buff[2], buf, stat->len);
                buff[stat->len + 2 + 0] = (uint8_t) (crc16 >> 8);
                buff[stat->len + 2 + 1] = (uint8_t) (crc16 >> 0);
                if (env2_flash_write(stat->addr, buff, stat->len + 4) != 0)
                    err = ENV2_ERR_IO;
                else
                    err = ENV2_ERR_OK;
                mem_free(buff);
            }
            else
                err = ENV2_ERR_NOMEM;
			break;
		}
		else
			break;
	}

    if (val != NULL)
    {
        if (*size >= stat->len)
              memcpy(val, buf, stat->len);
        else
            err = ENV2_ERR_INVALID;
        mem_free(buf);
    }
    else
    {
       if (env2_data != NULL)
           mem_free(env2_data);
       env2_data = buf;
       env2_dlen = stat->len;
    }

    if (err == ENV2_ERR_OK && size != NULL)
		*size = stat->len;
	return (err);
}

env2_err_t env2_save(int no, uint8_t area, void *val, uint8_t len)
{
	uint16_t crc16;
	uint8_t *buf;
    env2_err_t err;

    if (no < 0 || ENV2_SECTOR_START + no > ENV2_SECTOR_END || area >= ENV2_AREA
            || !val || !len)
        return (ENV2_ERR_INVALID);

    buf = (uint8_t*) mem_malloc(len + 4);
	if (buf == NULL)
        return (ENV2_ERR_NOMEM);

    buf[0] = ENV2_MAGIC;
    buf[1] = len;
    memcpy(&buf[2], val, len);
    crc16 = crc16_ccit_calc_s(&buf[2], len);
    buf[2 + len + 0] = (uint8_t) (crc16 >> 8);
    buf[2 + len + 1] = (uint8_t) (crc16 >> 0);
    err = _env2_save_r(no, area, buf, len + 4);
	mem_free(buf);
	return (err);
}

env2_err_t env2_del(int no)
{
    uint16_t sector = ENV2_SECTOR_START + no;

    if (sector > ENV2_SECTOR_END)
        return (ENV2_ERR_INVALID);

    if (_env2_del == no)/* 防止连续调用env_flash_erase_sector() */
        return (ENV2_ERR_OK);

    _env2_del = no;
    if (env2_flash_erase_sector(sector) == 0)
        return (ENV2_ERR_OK);
    return (ENV2_ERR_IO);
}

env2_err_t env2_delete(int no, uint8_t area)
{
    uint8_t i;
    uint8_t len[ENV2_AREA];
    uint8_t *data[ENV2_AREA];
    uint16_t sector = ENV2_SECTOR_START + no;

    if (sector > ENV2_SECTOR_END || area >= ENV2_AREA)
        return (ENV2_ERR_INVALID);

    if (_env2_delete == no)/* 防止连续调用env_flash_erase_sector() */
        return (ENV2_ERR_OK);

    memset(len, 0, sizeof(len));
    memset(data, 0, sizeof(data));
    for (i = 0; i < ENV2_AREA; ++i)
    {
        if (i != area)
        {
            if (env2_load(no, i, NULL, NULL) == ENV2_ERR_OK)
                env2_dup(&data[i], &len[i]);
        }
    }

    _env2_delete = no;
    if (env2_flash_erase_sector(sector) == 0)
    {
        for (i = 0; i < ENV2_AREA; ++i)
        {
            if (data[i] != NULL)
            {
                env2_save(no, i, data[i], len[i]);
                mem_free(data[i]);
            }
        }
        return (ENV2_ERR_OK);
    }
    return (ENV2_ERR_IO);
}

/*----------------------------------------------------------------------------*/
static env2_err_t _env2_save_r(int no, uint8_t area, void *val, uint8_t len)
{
    env2_stat_t *stat;
	uint32_t addr;
    uint32_t end;

    if (no < 0 || ENV2_SECTOR_START + no > ENV2_SECTOR_END || area >= ENV2_AREA
            || !val || len < 2)
        return (ENV2_ERR_INVALID);

    end = (ENV2_SECTOR_START + no) * ENV2_SECTOR_SZ;
    end += (uint32_t) (area + 1) * ENV2_AREA_SZ;
    stat = env2_stat(no, area);
	if (stat == NULL)
	{
        env2_delete(no, area);
        addr = end - ENV2_AREA_SZ;
	}
	else
    {
        addr = stat->addr + stat->len + 2;
        if (addr + len >= end)/* 溢出 */
        {
            env2_delete(no, area);
            addr = end - ENV2_AREA_SZ;
        }
	}

    if (_env2_delete == no)
        _env2_delete = -1;

    if (env2_flash_write(addr, val, len) != 0)
        return (ENV2_ERR_IO);
    return (ENV2_ERR_OK);
}

static env2_stat_t *env2_stat(int no, uint8_t area)
{
    static env2_stat_t stat;

    if (no < 0 || ENV2_SECTOR_START + no > ENV2_SECTOR_END || area >= ENV2_AREA)
		return (NULL);

    switch (env2_addr(no, area, -1, &stat))
	{
	case 0:
		return &stat;
	case 1:/* 数据长度异常需要修复 */
		for (; stat.r != 0;)
		{
			if (--stat.r == 0)
				break;

            if (env2_addr(no, area, stat.r, &stat) == 0)
			{
                env2_delete(no, area);
				return (&stat);
			}
		}
        env2_delete(no, area);
		break;
	case 2:/* 数据损坏 */
        if (env2_repair(no, area, &stat) == 0)/* 尝试修复数据 */
		{
			stat.r = 1;
            stat.addr = (ENV2_SECTOR_START + no) * ENV2_SECTOR_SZ;
            stat.addr += (uint32_t) area * ENV2_AREA_SZ + 2;
			return (&stat);
		}
        env2_delete(no, area);
		break;
	default:
		break;
	}
	return (NULL);
}

static int env2_addr(int no, uint8_t area, int n, env2_stat_t *stat)
{
    uint32_t addr;
    uint32_t end;
	uint8_t magic[4];

    if (no < 0 || ENV2_SECTOR_START + no > ENV2_SECTOR_END ||  area >= ENV2_AREA
            || !stat)
		return (-1);/* 参数错误 */

    memset(stat, 0, sizeof(env2_stat_t));
    addr = (ENV2_SECTOR_START + no) * ENV2_SECTOR_SZ;
    addr += (uint32_t) area * ENV2_AREA_SZ;
    end = addr + ENV2_AREA_SZ;

	memset(magic, 0, 4);
    env2_flash_read(addr, magic, 4);
    while (magic[0] == ENV2_MAGIC)
	{
		++stat->r;
        stat->addr = addr + 2;
        stat->len = magic[1];

		if (n > 0 && stat->r == n)
			return (0);

        addr = stat->addr + stat->len + 2;
        if (addr >= end)
			return (1);/* 数据长度异常需要修复 */

		memset(magic, 0, 4);
        env2_flash_read(addr, magic, 4);
		if (memcmp(magic, "\xFF\xFF\xFF\xFF", 4) == 0)
			return (0);
	}

	if (stat->r == 0)
	{
		if (memcmp(magic, "\xFF\xFF\xFF\xFF", 4) != 0)
            env2_delete(no, area);
		return (-1);/* 无有效数据 */
	}
	return (2);/* 数据损坏 */
}

static int env2_repair(int no, uint8_t area, env2_stat_t *stat)
{
	uint8_t *buf;

    if (no < 0 || ENV2_SECTOR_START + no > ENV2_SECTOR_END || !stat)
		return (-1);/* 参数错误 */

    buf = (uint8_t*) mem_calloc(1, stat->len + 4);
	if (buf == NULL)
		return (-1);/* 内存不足修复失败 */

    if (env2_flash_read(stat->addr, &buf[2], stat->len + 2) == 0)
	{
        if (crc16_ccit_calc_s(&buf[2], stat->len + 2) == 0)
		{
            env2_delete(no, area);
            buf[0] = ENV2_MAGIC;
            buf[1] = stat->len;
            if (_env2_save_r(no, area, buf, stat->len + 4) == ENV2_ERR_OK)
			{
				mem_free(buf);
				return (0);
			}
		}
		else
            env2_delete(no, area);
	}
	else
        env2_delete(no, area);
	mem_free(buf);
	return (-1);
}

env2_err_t env2_dup(uint8_t **data, uint8_t *len)
{
    if (!data || !len)
        return ENV2_ERR_INVALID;

    if (env2_data != NULL)
    {
        *len = env2_dlen;
        *data = (uint8_t*) mem_malloc(*len);
        if (*data == NULL)
            return ENV2_ERR_NOMEM;
        memcpy(*data, env2_data, *len);
        mem_free(env2_data);
        env2_data = NULL;
        return ENV2_ERR_OK;
    }
    return ENV2_ERR_INVALID;
}
